﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using KTOP.Core.Domain;
using KTOP.Core.Domain.Matrix;
using KTOP.Core.Extensions;
using KTOP.Core.Formatters;
using KTOP.Core.Netlists;
using KTOP.Homeworks.Hw4.Logics.DijkstraRoutes;
using KTOP.Homeworks.Hw4.Logics.MaxThroughputRoute;

namespace KTOP.Homeworks.Hw4.Logics
{
    internal class MaxRouteBuilder
    {
        public class Route
        {
            public List<int> Nodes { get; set; }
            public int Length { get; set; }

            public Route Fork(int node, int addlen)
            {
                return new Route()
                           {
                               Nodes = Nodes.Concat(ExtEnumerable.One(node)).ToList(),
                               Length = Length + addlen
                           };
            }
        }

        public class RouteToken
        {
            public int Max { get; set; }
            public Route Route { get; set; }
        }


        private static void Iteration(int start, int end, RouteToken token, /*List<Route> routes,*/Route currentRoute, CMatrix cMatrix)
        {
            if (start == end)
            {
                if (currentRoute.Length > token.Max)
                {
                    token.Route = currentRoute;
                    token.Max = currentRoute.Length;

                    Console.WriteLine("{0:D4} {1}", currentRoute.Length,
                                      string.Join("->", currentRoute.Nodes.Select(n => n + 1)));
                }

                return;
            }

            for (var i = 0; i < cMatrix.Size; i++)
            {
                if (i != start && !currentRoute.Nodes.Contains(i) && cMatrix[start, i] != 0)
                {
                    Iteration(i, end, token, currentRoute.Fork(i, cMatrix[start, i]), cMatrix);
                }
            }
        }

        public static Route BuildMaxRoute(int start, int end, CMatrix cMatrix)
        {
            var token = new RouteToken() {Max = int.MinValue};
            var croute = new Route() {Length = 0, Nodes = new List<int>() {start}};

            Iteration(start, end, token, croute, cMatrix);

            return token.Route;
        }
    }

    class HwCreationSession
    {
        private readonly Netlist _netlist;
        private RMatrix _rMatrix;
        private BoardPlacer _placer;
        private DMatrix _dMatrix;
        private int[] _renamingTable;
        private CMatrix _cMatrix;
        private readonly TextWriter _tw;
        private List<RouteLabel[]> _routes;
        private MaxRouteBuilder.Route _maxRoute;

        public HwCreationSession(Netlist netlist, TextWriter tw)
        {
            _netlist = netlist;
            _tw = tw;
        }

        public void Do()
        {
            _placer = new BoardPlacer(_netlist.ModuleCount);

            _rMatrix = RMatrix.CreateFromNetlist(_netlist);
            _dMatrix = DMatrix.CreateFromBoardPlacer(_placer);

            var rMatrixSumms = _rMatrix.Summs.Select((s, i) => new {Summ = s, Module = i}).OrderBy(k => k.Summ);
            var dMatrixSumms = _dMatrix.Summs.Select((s, i) => new {Summ = s, Module = i}).OrderByDescending(k => k.Summ);

            _renamingTable = rMatrixSumms
                .Zip(dMatrixSumms, (rm, dm) => new {E = rm.Module, P = dm.Module})
                .OrderBy(k => k.E)
                .Select(k => k.P)
                .ToArray();

            _cMatrix = CMatrix.Create(_rMatrix, _dMatrix, _renamingTable);
            _routes = DijkstraRouteBuilder.FindRoutes(_cMatrix);

            //_maxRoute = MaxRouteBuilder.BuildMaxRoute(0, _cMatrix.Size - 1, _cMatrix);

            MaxThroughputRouteBuilder.FrankFricheIteration(0, _cMatrix.Size-1, _cMatrix);

            FormatResults(_tw);

        }

        void FormatResults(TextWriter tw)
        {
            var matrixFormatter = new MatrixFormatter();
            var netlistFormatter = new NetlistFormatter();
            var textTableFormatter = new TextTableFormatter();

            netlistFormatter.Format(_netlist, "source netlist", tw);
            tw.WriteLine();
        
            matrixFormatter.Format(_rMatrix, "r matrix", tw);
            tw.WriteLine();

            matrixFormatter.Format(_dMatrix, "d matrix", tw);
            tw.WriteLine();

            var renamingTable = new[]
                                    {
                                        new[] {"module"}.Concat(Enumerable.Range(1, _netlist.ModuleCount).Select(t => t.ToString())).ToArray(),
                                        new[] {"position"}.Concat(_renamingTable.Select(t => (t+1).ToString())).ToArray()
                                    };

            tw.WriteLine("position table");
            textTableFormatter.Format(tw, renamingTable, TextTableFormatter.Borders.None);
            tw.WriteLine();

            matrixFormatter.Format(_cMatrix, "c matrix", tw);
            tw.WriteLine();

            var rows = _routes.First().Length;
            var cols = _routes.Count;
            var body = Enumerable.Range(0, rows).Select(row => Enumerable.Range(0, cols).Select(col => _routes[col][row]).ToArray()).ToArray();
            body = body
                .Select(row => row.TakeWhile(e => !e.IsConstant)
                                   .Concat(ExtEnumerable.One(row.First(e => e.IsConstant)))
                                   .ToArray())
                .ToArray();

            var header = ExtEnumerable.One(ExtEnumerable.One("").Concat(Enumerable.Range(1, cols).Select(c => c.ToString())).ToArray());
            var routeTableBody = header.Concat(
                body.Select((r, i) => ExtEnumerable.One((i + 1).ToString())
                    .Concat(r.Select(rr => rr.ToString())).ToArray())).ToArray();


            tw.WriteLine("route table");
            tw.WriteLine();
            textTableFormatter.Format(tw, routeTableBody, TextTableFormatter.Borders.None);
            tw.WriteLine();

            tw.WriteLine("max route: {0}", string.Join("->", _maxRoute.Nodes.Select(n => n + 1)));
            tw.WriteLine("length: {0}", _maxRoute.Length);

        }

    }
}
